#include "DataContainer.h"

namespace PComunicator
{

DataContainer::DataContainer(void)
{
  	myCommands.insert(std::make_pair("convert",'c'));
	myCommands.insert(std::make_pair("highlight",'h'));
	myCommands.insert(std::make_pair("newkey",'n'));
	myCommands.insert(std::make_pair("alive",'a'));

	myCommands.insert(std::make_pair("disconnect",'d'));
	myCommands.insert(std::make_pair("error",'e'));
	myCommands.insert(std::make_pair("sending",'s'));
	myCommands.insert(std::make_pair("receive",'r'));
	myCommands.insert(std::make_pair("yes",'y'));
}


DataContainer::~DataContainer(void)
{
}


bool DataContainer::AddCommand(std::string theCommand, bool theOverwrite)
{
	if( theCommand.empty() )
	{
		return false;
	}


	std::map<std::string,char,std::greater<std::string> >::iterator it;
	bool found = false;
	for( it = myCommands.begin() ; it != myCommands.end(); it++)
	{
			if( ((*it).first) == theCommand)
			{
				found = true;
				break;
			}
	}

	if(!found)
		return false;
	else
	{
		if(theOverwrite)
		{
			myActualCommand = (*it).second;
			return true;
		}
		else
		{
				myActualCommand = (*it).second;
				return true;
		}
	}
}

bool DataContainer::GenerateOutput(std::vector<byte> &theOutput)
{
	if(myActualCommand == 0 )
		return false;

	std::vector<byte> aFinalOutput;
	theOutput.clear();

	//najprv sa do celkoveho pola prida prikaz,ktory tvori zaciatok pola
	aFinalOutput.push_back(static_cast<byte>(myActualCommand));

	//zistime ci potrebujeme datovu sekciu alebo ne
	if(myContainer.size() == 0)  /*ak ne, navratime pole o dlzky jedna */
	{
		std::copy(aFinalOutput.begin(),aFinalOutput.end(),std::back_inserter(theOutput));
		return true;
	}
	else //datovy kontajner neni prazdny preto vytvarame aj datovu sekciu
	{
		//zakodujeme info o pocte datovych jednotiek do 4 byte a pridame do pola
		std::vector<byte> aDataCountInfo = PConvertor::FromBasicType<unsigned int>(myContainer.size());
		std::copy(aDataCountInfo.begin(),aDataCountInfo.end(),std::back_inserter(aFinalOutput));

		//postupne kodujeme do pola byte datove jednotky v samotnom kontajnery a pridavame ich
		//do konecneho pola
		for(unsigned int i=0; i < myContainer.size(); i++)
		{
			std::vector<byte> aTempVector = DataFormater(myContainer[i]);
			std::copy(aTempVector.begin(),aTempVector.end(),std::back_inserter(aFinalOutput));
		}

		std::copy(aFinalOutput.begin(),aFinalOutput.end(),std::back_inserter(theOutput));
		return true;
	}

	return false;
}

bool DataContainer::Parse(const std::vector<byte> &theInput)
{
	byte aReceivedCommand = 0;
	std::vector<byte> aHelperA;
	std::vector<boost::any> aResultDataA;

	if(theInput.empty())
	{
		return false;
	}

	aHelperA.push_back(theInput[0]);
	aReceivedCommand = PConvertor::ToBasicType<byte>(aHelperA);
	if(aReceivedCommand != 0 )
	{
			myActualCommand = static_cast<char>(aReceivedCommand);  //nastavime prijaty prikaz
			myContainer.clear();	//vymazeme pre istotu kontajner lebo ziadne data sme neprijali

	}

	if(theInput.size() == 1 )	//ak velkost pola je jedna(len prikaz sa poslal) tak koncime
	{
		return true;
	}
	else
	{
		std::vector<byte>::const_iterator iterStart = theInput.begin();
		std::vector<byte>::const_iterator iterEnd = iterStart;
		//ziskame pocet dat ktore su zakodovane
		aHelperA.clear();

		aHelperA.assign(++iterStart,iterEnd+=5);	//nahrame do pomocneho pola nasledujuce 4 byte ktore koduju pocet dat
		unsigned int aDataCount = PConvertor::ToBasicType<unsigned int>(aHelperA); //prekonvertujeme cast pola v ktorom je ulozeny pocet dat
		if( aDataCount <=0 )
			return false;

		while(myContainer.size() != aDataCount)
		{
			iterStart = iterEnd;
			iterEnd +=1;
			byte aDataTypeCode = (*iterStart); // ziskame z pola datovy typ ktory je dalej kodovany v poly byte

			aHelperA.clear();
			iterStart+=1;
			iterEnd+=4;		//nacitame dalsie 4 byte
			aHelperA.assign(iterStart,iterEnd);
			unsigned int aDataLenght = PConvertor::ToBasicType<unsigned int>(aHelperA); // z nacitanych 4byte si zistime dlzku dat ktore musime nacitat

			aHelperA.clear();
			iterStart = iterEnd;
			iterEnd += aDataLenght; //nacitame presne tolko kolko byte ako su velke data
			aHelperA.assign(iterStart,iterEnd);
			boost::any aResult;
			if(GetData(aHelperA,aDataTypeCode,aResult)) //prekonvertujeme pole byte na konkretne data, a ak sato podari tak pridame do kontajnera
			{
				myContainer.push_back(aResult);
			}
		}//end of while
		return true;
	}//end of else
	return false;
}


char DataContainer::GetCommand()
{
	return myActualCommand;
}


/********************************************************************/
/*****PRIVATE METHODS************************************************/


char DataContainer::GetDatatypeCode(boost::any theData)
{
	if(theData.type() == typeid(int))
	{
		return 1;			//int datatype has the code value of 1
	}
	else if(theData.type() == typeid(char))
	{
		return 2;			//char datatype has the code value of 2
	}
	else if(theData.type() == typeid(byte))
	{
		return 3;			//unsigned char(byte) has the code value of 3
	}
	else if(theData.type() == typeid(std::string))
	{
		return 4;			//string has the code value of 4
	}
	else if(theData.type() == typeid(std::wstring))
	{
		return 5;			//wstring (unicode string) has the code value 5
	}
	else if(theData.type() == typeid(long))
	{
		return 6;
	}
	else if(theData.type() == typeid(long long))
	{
		return 7;
	}
	else if(theData.type() == typeid(PFileInfo))
	{
		return 8;			//PFileInfo has the code value 6
	}
	else
	{
		throw UnknownTypeException(theData); //in any other case we are dealing with an unknown=unsuported data type
	}
}


std::vector<byte> DataContainer::DataFormater(boost::any theData)
{
	byte aDataTypeCode;
	unsigned int aDataLength;
	std::vector<byte> aEncodedData;

	aDataTypeCode = GetDatatypeCode(theData);
	if(aDataTypeCode == 1 ) // jedna sa o int
	{
		aEncodedData = PConvertor::FromBasicType<int>(any_cast<int>(theData));
	}
	else if(aDataTypeCode == 6 ) //jedna sa o long
	{
		aEncodedData = PConvertor::FromBasicType<long>(any_cast<long>(theData));
	}
	else if(aDataTypeCode == 7)	//jedna sa o long long
	{
		aEncodedData = PConvertor::FromBasicType<long long>(any_cast<long long>(theData));
	}
	else if(aDataTypeCode == 4) //jedna sa o string
	{
		aEncodedData = PConvertor::FromString(any_cast<std::string>(theData));
	}
	else if(aDataTypeCode == 5) //jedna sa o wstring
	{
		aEncodedData = PConvertor::FromString(any_cast<std::wstring>(theData));
	}
	else if(aDataTypeCode == 8) // PFileInfo
	{
		PFileInfo aInfo = any_cast<PFileInfo>(theData);
		aEncodedData = aInfo.FormatToArray();
	}

	aDataLength = aEncodedData.size();
	std::vector<byte> aEncodedDataLenght = PConvertor::FromBasicType<int>(aDataLength);

	//vytvorime jednu sekciu dat tj 1byt(datovytyp)4byte(velkost dat)XXbyte(samotne data)
	std::vector<byte> aResult;

	aResult.push_back(aDataTypeCode);	//pridame datovy typ
	std::copy(aEncodedDataLenght.begin(),aEncodedDataLenght.end(),std::back_inserter(aResult)); //pridame dlzku samotnych dat
	std::copy(aEncodedData.begin(),aEncodedData.end(),std::back_inserter(aResult));	//pridame samotne data

	return aResult;	//navratime formatovane pole,ktore obsahuje info o type a dlzke + samotne data
}

bool DataContainer::GetData(std::vector<byte> theInput,byte theType,boost::any &aData)
{
	if(theType == 1 ) // jedna sa o int
	{
		aData = PConvertor::ToBasicType<int>(theInput);
		return true;
	}
	else if(theType == 6 ) //jedna sa o long
	{
		aData = PConvertor::ToBasicType<long>(theInput);
		return true;
	}
	else if(theType == 7)	//jedna sa o long long
	{
		aData = PConvertor::ToBasicType<long long>(theInput);
		return true;
	}
	else if(theType == 4) //jedna sa o string
	{
		aData = PConvertor::ToString(theInput);
		return true;
	}
	else if(theType == 5) //jedna sa o wstring
	{
		aData = PConvertor::ToWString(theInput);
		return true;
	}
	else if(theType == 8) // PFileInfo
	{
		PFileInfo aInfo(theInput);
		aData = aInfo;
		return true;
	}

	return false;
}

}


